/*
 * InputState
 * 
 * Helper class to get keyboard, mouse and button state.
 * 
 * (C) Marcus Pflanz 2010
 * 
 * */

namespace CGShaderIntegration
{
    #region Using Statements
    using System;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Input;
    #endregion

    /// <summary>
    /// Helper for reading input from keyboard and gamepad. This class tracks both
    /// the current and previous state of both input devices, and implements query
    /// properties for high level input actions such as "move up through the menu"
    /// or "pause the game".
    /// </summary>
    /// <remarks>Based on a similar class in the Game State Management sample.</remarks>
    public class CGInputManager : GameComponent
    {
        /// <summary>
        /// Directions for move
        /// </summary>
        public enum Direction
        {
            /// <summary>
            /// move left
            /// </summary>
            Left,
            /// <summary>
            /// move right
            /// </summary>
            Right,
            /// <summary>
            /// move up
            /// </summary>
            Up,
            /// <summary>
            /// move down
            /// </summary>
            Down,
            /// <summary>
            /// no mve
            /// </summary>
            None // this is used for questioning of which direction currently is pressed
        }

        #region Fields
        /// <summary>
        /// maximal number of inputs
        /// </summary>
        public const int MaxInputs = 4;

        /// <summary>
        /// state of keyboard
        /// </summary>
        public readonly KeyboardState[] CurrentKeyboardStates;
        /// <summary>
        /// state of gamepad
        /// </summary>
        public readonly GamePadState[] CurrentGamePadStates;

        /// <summary>
        /// state of keyboard
        /// </summary>
        public readonly KeyboardState[] LastKeyboardStates;
        /// <summary>
        /// state of state of game pad
        /// </summary>
        public readonly GamePadState[] LastGamePadStates;

        /// <summary>
        /// timer for rumbler
        /// </summary>
        public float[] RumbleTimer;
        /// <summary>
        /// timer for rumbler end
        /// </summary>
        public float[] RumbleTimerEnd;
        /// <summary>
        /// value of rumble
        /// </summary>
        public float[,] RumbleValues;
        /// <summary>
        /// state of rumbler
        /// </summary>
        public bool[] IsRumbling;

        #endregion

        #region Initialization

        /// <summary>
        /// Initializes a new instance of the InputManager class.
        /// </summary>
        /// <param name="game">Game that the game component should be attached to.</param>
        public CGInputManager(Game game) 
            : base(game)
        {
            CurrentKeyboardStates = new KeyboardState[MaxInputs];
            CurrentGamePadStates = new GamePadState[MaxInputs];

            LastKeyboardStates = new KeyboardState[MaxInputs];
            LastGamePadStates = new GamePadState[MaxInputs];

            IsRumbling = new bool[4];
            RumbleTimer = new float[4];
            RumbleTimerEnd = new float[4];
            RumbleValues = new float[4, 2];
           
            for (int i = 0; i < 4; i++)
            {
                IsRumbling[i] = false;
                RumbleTimer[i] = 0;
                RumbleTimerEnd[i] = 0;
                for (int j = 0; j < 2; j++)
                {
                    RumbleValues[i, j] = 0f;
                }
            }
        }

        #endregion

        #region Properties



        /// <summary>
        /// Gets a value indicating whether [button left].
        /// </summary>
        /// <value><c>true</c> if [button left]; otherwise, <c>false</c>.</value>
        public bool ButtonLeft
        {
            get
            {
                return IsNewKeyPress(Keys.Left) ||
                       IsNewButtonPress(Buttons.DPadLeft) ||
                       IsNewButtonPress(Buttons.LeftThumbstickLeft);
            }
        }

        /// <summary>
        /// Lefts the stick.
        /// </summary>
        /// <param name="playerIndex">Index of the player.</param>
        /// <returns></returns>
        public Vector2 LeftStick(int playerIndex)
        {
            return CurrentGamePadStates[playerIndex].ThumbSticks.Left;
        }

        /// <summary>
        /// Rights the stick.
        /// </summary>
        /// <param name="playerIndex">Index of the player.</param>
        /// <returns></returns>
        public Vector2 RightStick(int playerIndex)
        {
            return CurrentGamePadStates[playerIndex].ThumbSticks.Left;
        }

        /// <summary>
        /// Gets a value indicating whether [button right].
        /// </summary>
        /// <value><c>true</c> if [button right]; otherwise, <c>false</c>.</value>
        public bool ButtonRight
        {
            get
            {
                return IsNewKeyPress(Keys.Right) ||
                       IsNewButtonPress(Buttons.DPadRight) ||
                       IsNewButtonPress(Buttons.LeftThumbstickRight);
            }
        }

        /// <summary>
        /// Gets the menu button pressed.
        /// </summary>
        /// <value>The menu button pressed.</value>
        public int MenuButtonPressed
        {
            get
            {
                if (ButtonLeft)
                {
                    return 1;
                }
                else if (ButtonRight)
                {
                    return 2;
                }
                else if (MenuSelect)
                {
                    return 3;
                }
                else if (MenuCancel)
                {
                    return 4;
                }
                else
                {
                    return 0;
                }
            }
        }

        /// <summary>
        /// Checks for a "menu select" input action, from any player,
        /// on either keyboard or gamepad.
        /// </summary>
        public bool MenuSelect
        {
            get
            {
                return IsNewKeyPress(Keys.Space) ||
                       IsNewKeyPress(Keys.Enter) ||
                       IsNewButtonPress(Buttons.A) ||
                       IsNewButtonPress(Buttons.Start);
            }
        }


        /// <summary>
        /// Checks for a "menu cancel" input action, from any player,
        /// on either keyboard or gamepad.
        /// </summary>
        public bool MenuCancel
        {
            get
            {
                return IsNewKeyPress(Keys.Escape) ||
                       IsNewButtonPress(Buttons.B) ||
                       IsNewButtonPress(Buttons.Back);
            }
        }


        /// <summary>
        /// Checks for a "exit game" input action, from any player,
        /// on either keyboard or gamepad.
        /// </summary>
        public bool ExitGame
        {
            get
            {
                return IsNewKeyPress(Keys.Escape) ||
                       IsNewButtonPress(Buttons.Back) ||
                       IsNewButtonPress(Buttons.Start);
            }
        }


       


        #endregion

        #region Methods
        /// <summary>
        /// Reads the latest state of the keyboard and gamepad.
        /// </summary>
        /// <param name="gameTime">Time elapsed since the last call to Update</param>
        public override void Update(GameTime gameTime)
        {
            for (int i = 0; i < MaxInputs; i++)
            {
                LastKeyboardStates[i] = CurrentKeyboardStates[i];
                LastGamePadStates[i] = CurrentGamePadStates[i];

                CurrentKeyboardStates[i] = Keyboard.GetState((PlayerIndex)i);
                CurrentGamePadStates[i] = GamePad.GetState((PlayerIndex)i);
            }
        }


        /// <summary>
        /// Helper for checking if a key was pressed during this update,
        /// by any player.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <returns>
        ///      <c>true</c> if [is key press] [the specified key]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsKeyPress(Keys key)
        {
            for (int i = 0; i < MaxInputs; i++)
            {
                if (IsKeyPress(key, (PlayerIndex)i))
                {
                    return true;
                }
            }

            return false;
        }


        /// <summary>
        /// Helper for checking if a key was newly pressed during this update,
        /// by any player.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <returns>
        ///      <c>true</c> if [is new key press] [the specified key]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsNewKeyPress(Keys key)
        {
            for (int i = 0; i < MaxInputs; i++)
            {
                if (IsNewKeyPress(key, (PlayerIndex)i))
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Helper for checking if a key was pressed during this update,
        /// by the specified player.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="playerIndex">Index of the player.</param>
        /// <returns>
        ///     <c>true</c> if [is key press] [the specified key]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsKeyPress(Keys key, PlayerIndex playerIndex)
        {
            return CurrentKeyboardStates[(int)playerIndex].IsKeyDown(key);
        }


        /// <summary>
        /// Helper for checking if a key was newly pressed during this update,
        /// by the specified player.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="playerIndex">Index of the player.</param>
        /// <returns>
        ///     <c>true</c> if [is new key press] [the specified key]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsNewKeyPress(Keys key, PlayerIndex playerIndex)
        {
            return (CurrentKeyboardStates[(int)playerIndex].IsKeyDown(key) &&
                    LastKeyboardStates[(int)playerIndex].IsKeyUp(key));
        }


        /// <summary>
        /// Helper for checking if a button was pressed during this update,
        /// by any player.
        /// </summary>
        /// <param name="button">The button.</param>
        /// <returns>
        ///     <c>true</c> if [is button press] [the specified button]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsButtonPress(Buttons button)
        {
            for (int i = 0; i < MaxInputs; i++)
            {
                if (IsButtonPress(button, (PlayerIndex)i))
                {
                    return true;
                }
            }

            return false;
        }


        /// <summary>
        /// Helper for checking if a button was newly pressed during this update,
        /// by any player.
        /// </summary>
        /// <param name="button">The button.</param>
        /// <returns>
        ///     <c>true</c> if [is new button press] [the specified button]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsNewButtonPress(Buttons button)
        {
            for (int i = 0; i < MaxInputs; i++)
            {
                if (IsNewButtonPress(button, (PlayerIndex)i))
                {
                    return true;
                }
            }

            return false;
        }


        /// <summary>
        /// Helper for checking if a button was pressed during this update,
        /// by the specified player.
        /// </summary>
        /// <param name="button">The button.</param>
        /// <param name="playerIndex">Index of the player.</param>
        /// <returns>
        ///    <c>true</c> if [is button press] [the specified button]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsButtonPress(Buttons button, PlayerIndex playerIndex)
        {
            return CurrentGamePadStates[(int)playerIndex].IsButtonDown(button);
        }


        /// <summary>
        /// Helper for checking if a button was newly pressed during this update,
        /// by the specified player.
        /// </summary>
        /// <param name="button">The button.</param>
        /// <param name="playerIndex">Index of the player.</param>
        /// <returns>
        ///     <c>true</c> if [is new button press] [the specified button]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsNewButtonPress(Buttons button, PlayerIndex playerIndex)
        {
            return (CurrentGamePadStates[(int)playerIndex].IsButtonDown(button) &&
                    LastGamePadStates[(int)playerIndex].IsButtonUp(button));
        }


        /// <summary>
        /// Checks for a "menu select" input action from the specified player.
        /// </summary>
        /// <param name="playerIndex">Index of the player.</param>
        /// <returns>
        ///     <c>true</c> if [is menu select] [the specified player index]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsMenuSelect(PlayerIndex playerIndex)
        {
            return IsNewKeyPress(Keys.Space, playerIndex) ||
                   IsNewKeyPress(Keys.Enter, playerIndex) ||
                   IsNewButtonPress(Buttons.A, playerIndex) ||
                   IsNewButtonPress(Buttons.Start, playerIndex);
        }

        /// <summary>
        /// Checks if any direction is pressed and if so what direction is pressed.
        /// </summary>
        /// <param name="playerIndex">Player index to check.</param>
        /// <returns>
        /// Direction player is pushing to or Direction.None if no direction is pushed towards.
        /// </returns>
        public Direction GetCurrentDirectionPressed(PlayerIndex playerIndex)
        {
            if (this.IsDirectionPressed(playerIndex, Direction.Up))
            {
                return Direction.Up;
            }
            else if (this.IsDirectionPressed(playerIndex, Direction.Right))
            {
                return Direction.Right;
            }
            else if (this.IsDirectionPressed(playerIndex, Direction.Left))
            {
                return Direction.Left;
            }
            else if (this.IsDirectionPressed(playerIndex, Direction.Down))
            {
                return Direction.Down;
            }
            else
            {
                return Direction.None;
            }
        }

        public bool IsDirectionPressed(PlayerIndex playerIndex, Direction direction)
        {
#if !XBOX
            switch (direction)
            {
                case Direction.Down:
                    return this.IsKeyPress(Keys.Down, playerIndex) || this.IsKeyPress(Keys.S, playerIndex);
                case Direction.Left:
                    return this.IsKeyPress(Keys.Left, playerIndex) || this.IsKeyPress(Keys.A, playerIndex);
                case Direction.Right:
                    return this.IsKeyPress(Keys.Right, playerIndex) || this.IsKeyPress(Keys.D, playerIndex);
                case Direction.Up:
                    return this.IsKeyPress(Keys.Up, playerIndex) || this.IsKeyPress(Keys.W, playerIndex);
                default:
                    throw new NotImplementedException("unknown direction: " + direction.ToString());
            }
#else
            switch (direction)
            {
                case Direction.Down:
                    return this.IsButtonPress(Buttons.DPadDown, playerIndex) || this.IsButtonPress(Buttons.LeftThumbstickDown, playerIndex);
                case Direction.Left:
                    return this.IsButtonPress(Buttons.DPadLeft, playerIndex) || this.IsButtonPress(Buttons.LeftThumbstickLeft, playerIndex);
                case Direction.Right:
                    return this.IsButtonPress(Buttons.DPadRight, playerIndex) || this.IsButtonPress(Buttons.LeftThumbstickRight, playerIndex);
                case Direction.Up:
                    return this.IsButtonPress(Buttons.DPadUp, playerIndex) || this.IsButtonPress(Buttons.LeftThumbstickUp, playerIndex);
                default:
                    throw  NotImplementedException("unknown direction: " + direction.ToString());
            }
#endif
        }

        #endregion
    }
}
